/*
 * ThreeAddress.cpp
 *
 *  Created on: 24/05/2010
 *      Author: rodolfo
 */

#include "ThreeAddress.h"
using namespace std;

ThreeAddress::ThreeAddress() {
	tempInstructionCount = 0;
	lineCount = -1;
	basicBlocks = 0;
	threeAddressTable.clear();
	mapLabelPos.clear();
	mapBBlockPos.clear();
}

ThreeAddress::~ThreeAddress() {
	threeAddressTable.clear();
	//delete &threeAddressTable;
}

void ThreeAddress::print(){
	vector<ThreeAddressInstruction>::iterator it = threeAddressTable.begin();
	int lineNumber = 0;
	cout << endl;
	while ( it < threeAddressTable.end() ){
		(*it).print();
		it++;
	}
//	cout << endl << " Basic Blocks = " << basicBlocks << endl;
//	cout << "Map of labels and line Position: " << endl;
//	map<int,int>::iterator itMap = mapLabelPos.begin();
//	while ( itMap != mapLabelPos.end() ){
//		cout << " " << (*itMap).first << " => " << (*itMap).second << endl;
//		itMap++;
//	}
//	cout << "Map of Basics Blocks and line Position: " << endl;
//	map<int,int>::iterator itMapBB = mapBBlockPos.begin();
//	while ( itMapBB != mapBBlockPos.end() ){
//		cout << " " << (*itMapBB).first << " => " << (*itMapBB).second << endl;
//		itMapBB++;
//	}

}

const vector<ThreeAddressInstruction>& ThreeAddress::getThreeAddressTable() const {
	return threeAddressTable;
}

inline const string& ThreeAddress::binaryOp( BinaryExpression *bExp, string op ) {
	ThreeAddressInstruction *instruction = new ThreeAddressInstruction();
	string lhs, rhs, tName;
	tName = getTempInstructionName();
	rhs = expressionAdd( bExp->rhs );
	lhs = expressionAdd( bExp->lhs );
	instruction->setLhs( lhs );
	instruction->setRhs( rhs );
	instruction->setOp( op );
	instruction->setResult( tName );
	threeAddressTable.push_back( *instruction );
	++lineCount;
	return tName;
}
inline void ThreeAddress::assingmentOP( BinaryExpression *bExp, string op ) {
	ThreeAddressInstruction *instruction = new ThreeAddressInstruction();
	string result = expressionAdd( bExp->lhs );
	instruction->setResult( result );
	string rhs = expressionAdd( bExp->rhs );
	instruction->setOp( op );
	instruction->setRhs( rhs );
	threeAddressTable.push_back( *instruction );
	++lineCount;
}
const string& ThreeAddress::binaryExpressionAdd( BinaryExpression *bExp ) {
	switch ( bExp->op ) {
		case SIASSIG: {
			assingmentOP( bExp, string( "=" ) );
			break;
		}
		case DIASSIG: {
			assingmentOP( bExp, string( "/=" ) );
			break;
		}
		case REASSIG: {
			assingmentOP( bExp, string( "%=" ) );
			break;
		}
		case PLASSIG: {
			assingmentOP( bExp, string( "+=" ) );
			break;
		}
		case MIASSIG: {
			assingmentOP( bExp, string( "-=" ) );
			break;
		}
		case MUASSIG: {
			assingmentOP( bExp, string( "*=" ) );
			break;
		}
		case PLUSOP: {
			return binaryOp( bExp, string( "+" ) );
		}
		case MINOP: {
			return binaryOp( bExp, string( "-" ) );
		}
		case MULTOP: {
			return binaryOp( bExp, string( "*" ) );
		}
		case DIVOP: {
			return binaryOp( bExp, string( "/" ) );
		}
		case REMOP: {
			return binaryOp( bExp, string( "%" ) );
		}
		case EQOP: {
			return binaryOp( bExp, string( "==" ) );
		}
		case NEOP: {
			return binaryOp( bExp, string( "!=" ) );
		}
		case OROP: {
			return binaryOp( bExp, string( "||" ) );
		}
		case ANDOP: {
			return binaryOp( bExp, string( "&&" ) );
		}
		case LEOP: {
			return binaryOp( bExp, string( "<=" ) );
		}
		case LTOP: {
			return binaryOp( bExp, string( "<" ) );
		}
		case GEOP: {
			return binaryOp( bExp, string( ">=" ) );
		}
		case GTOP: {
			return binaryOp( bExp, string( ">" ) );
		}
	}
}
inline const string& ThreeAddress::unaryOp( UnaryExpression *unExp, string op ) {
	ThreeAddressInstruction *instruction = new ThreeAddressInstruction();
	string result;
	result = expressionAdd( unExp->expression ); 
	unExp->postfixOperator ? instruction->setLhs( result ) : instruction->setRhs( result );
	result = getTempInstructionName();
    instruction->setResult( result );
	instruction->setOp( op );
	threeAddressTable.push_back(*instruction);
	++lineCount;
	return result;

}
const string& ThreeAddress::unaryExpressionAdd( UnaryExpression *unExp ) {
	switch ( unExp->op ) {
		case UMINUS: {
			return unaryOp( unExp, string( "-" ) );
			break;
		}
		case UNEGAT: {
			return unaryOp( unExp, string( "!" ) );
			break;
		}
		case UPLUS: {
			return unaryOp( unExp, string( "+" ) );
			break;
		}
		case BITNOT: {
			return unaryOp( unExp, string( "~" ) );
			break;
		}
		case PLUSPLUS: {
			return unaryOp( unExp, string( "++" ) );
			break;
		}
		case MINUSMINUS: {
			return unaryOp( unExp, string( "--" ) );
			break;
		}
		case SZEOF: {
			return unaryOp( unExp, string( "SIZEOF" ) );
			break;
		}
	}
}
const string& ThreeAddress::castExpressionAdd( CastExpression *castExp ) {
	string rhs, lhs, result, op( "CAST" );
	rhs = expressionAdd( castExp->exp );
	switch ( castExp->type ) {
		case 'C': {
			lhs = string( "(char)" );
		}
		case 'S': {
			lhs = string( "(short)" );
		}
		case 'I': {
			lhs = string( "(int)" );
		}
		case 'L': {
			lhs = string( "(long)" );
		}
		case 'F': {
			lhs = string( "(float)" );
		}
		case 'D': {
			lhs = string( "(double)" );
		}
	}
	result = getTempInstructionName();
	ThreeAddressInstruction instruction( op, lhs, rhs, result );
	threeAddressTable.push_back( instruction );
	++lineCount;
	return result;
}
const string& ThreeAddress::expressionAdd( Expression *expression ) {
	switch ( expression->kind ) {
		case Expression::binExp: {
			BinaryExpression *bExp = static_cast<BinaryExpression*> ( expression );
			return binaryExpressionAdd( bExp );
			break;
		}
		case Expression::castExp: {
			CastExpression *cExp = static_cast<CastExpression*> ( expression );
			return castExpressionAdd( cExp );
			break;
		}
		case Expression::unExp: {
			return unaryExpressionAdd( static_cast<UnaryExpression*> ( expression ) );
			break;
		}
		case Expression::constantExp: {
			return expression->value;
			break;
		}
		case Expression::identifierExp: {
			Identifier *tempIdent = reinterpret_cast<Identifier*>(expression);
			return *(tempIdent->name);
			break;
		}
		case Expression::strExp: {
			return expression->value;
			break;
		}
	}

}

const string& ThreeAddress::conditionResult( Expression *condition ){
	string op, lhs, rhs;
	static string conditionalResult = "0";
	switch ( condition->kind ) {
		case Expression::binExp: {
			BinaryExpression *bExp = static_cast<BinaryExpression*> ( condition );
			op = changeOp( bExp->op );
			if ( op != "0"){
				lhs = expressionAdd( bExp->lhs );
				rhs = expressionAdd( bExp->rhs );
				conditionalResult = lhs + op + rhs;
			}
			return conditionalResult;
			break;
		}
		case Expression::castExp: {
			CastExpression *cExp = static_cast<CastExpression*> ( condition );
			castExpressionAdd( cExp );
			conditionalResult = "0";
			return conditionalResult;
			break;
		}
		case Expression::unExp: {
			unaryExpressionAdd( static_cast<UnaryExpression*> ( condition ) );
			conditionalResult = "0";
			return conditionalResult;
			break;
		}
		case Expression::constantExp: {
			if( condition->value == "0" )
				return conditionalResult = "1";
			conditionalResult = "0";
			return conditionalResult;
			break;
		}
		case Expression::identifierExp: {
			conditionalResult = "0";
			return conditionalResult;
			break;
		}
		case Expression::strExp: {
			conditionalResult = "0";
			return conditionalResult;
			break;
		}
	}
}

const string& ThreeAddress::changeOp( BinaryOperators op ){
	static string opChanged;
	switch ( op ) {
		case SIASSIG:
		case DIASSIG:
		case REASSIG: 
		case PLASSIG: 
		case MIASSIG: 
		case MUASSIG: 
		case PLUSOP: 
		case MINOP: 
		case MULTOP: 
		case DIVOP: 
		case REMOP: 
		case OROP:
		case ANDOP: {
			return opChanged = "0";
		}
		case EQOP: {
			return opChanged = "!=";
		   }
		case NEOP: {
			return opChanged = "==";
		}
		case LEOP: {
			return opChanged = ">";
		}
		case LTOP: {
			return opChanged = ">=";
		}
		case GEOP: {
			return opChanged = "<";
		}
		case GTOP: {
			return opChanged = "<=";
		}
	}
}

void ThreeAddress::makeTable( Statement* stm ) {
	string result; 
	string lhs = "if", rhs = "goto", blankString = "";
	string tempString;
	ThreeAddressInstruction instruction;
	instruction.setLhs( lhs );	
	instruction.setRhs( rhs );
	switch ( stm->kind ) {
		case Statement::expStm: {
			ExpressionStatement *expStmt = static_cast<ExpressionStatement*> ( stm );
			expressionAdd( expStmt->exp );
			break;
		}
		case Statement::ifStm: {
			If *ifStm = static_cast<If*> ( stm );
			int beginIf, endIf;
			result = conditionResult( ifStm->condition );
			instruction.setOp( result );
			threeAddressTable.push_back(instruction);
			beginIf = ++lineCount;
			makeTable( ifStm->command );
			endIf = lineCount;
			if ( ifStm->elseStatement != 0 ) {
				int beginElse, endElse;
//				result = "!(" + result + ")";
//				instruction.setOp( result );
//				instruction.setResult( tempString );
//				threeAddressTable.push_back(instruction);
                threeAddressTable.push_back( ThreeAddressInstruction( blankString, blankString, rhs, blankString ) );
				beginElse = ++lineCount + 1;
				makeTable( ifStm->elseStatement->command );
				endElse = lineCount;
				tempString = convertToStr( beginElse );
				(threeAddressTable[beginIf]).setResult( tempString );
				tempString = convertToStr( endElse + 1 );
				(threeAddressTable[(beginElse - 1)]).setResult( tempString );
			} else {
				tempString = convertToStr( (endIf + 1) );
				(threeAddressTable[beginIf]).setResult( tempString );
			} 
			break;
		}
		case Statement::doStm: {
			Do *doStm = static_cast<Do*> ( stm );
			int beginDo, endDo;
			result = conditionResult( doStm->condition );
			beginDo = lineCount + 1;
			makeTable( doStm->command );
			instruction.setOp( result );
			tempString = convertToStr((lineCount + 3));
			instruction.setResult( tempString );
			threeAddressTable.push_back( instruction );
			++lineCount;
			tempString = convertToStr(beginDo);
			threeAddressTable.push_back( ThreeAddressInstruction( blankString, blankString, rhs, tempString ) );
			endDo = ++lineCount;
			break;
		}
		case Statement::whileStm: {
			While *whileStm = static_cast<While*> ( stm );
			int beginWhile, endWhile;
			result = conditionResult ( whileStm->condition );
			instruction.setOp( result );
			threeAddressTable.push_back( instruction );
			beginWhile = ++lineCount; 
			makeTable ( whileStm->command );
			tempString = convertToStr(beginWhile);
			threeAddressTable.push_back( ThreeAddressInstruction( blankString, blankString, rhs, tempString ) );
			endWhile = ++lineCount;
			tempString = convertToStr((endWhile + 1) );
			(threeAddressTable[beginWhile]).setResult( tempString );
			break;
		}
		case Statement::forStm: {
			For *forStm = static_cast<For*> ( stm );
			int beginFor, endFor;
			expressionAdd( forStm->declaration->exp );
			string result = conditionResult( forStm->condition->exp );
			instruction.setOp( result );
			threeAddressTable.push_back( instruction );
			beginFor = ++lineCount; 
			expressionAdd( forStm->inc );
			makeTable( forStm->command );
			tempString = convertToStr( beginFor );
			threeAddressTable.push_back( ThreeAddressInstruction( blankString, blankString, rhs, tempString ) );
			endFor = ++lineCount;
			tempString = convertToStr( endFor + 1);
			(threeAddressTable[beginFor]).setResult( tempString );
			break;
		}
		case Statement::compoundStm: {
			CompoundStatement *compoundStm = static_cast<CompoundStatement*> ( stm );
			Statements::iterator it = compoundStm->statementList->begin();
			while ( it != compoundStm->statementList->end() ) {
				makeTable((*it));
				it++;
			}
		}
	}
}

string ThreeAddress::convertToStr(int value ){
	stringstream ss;
	ss << value;
	return ss.str();
}
int ThreeAddress::convertFromStr( string value ){
	stringstream ss(value);
	int integerValue;
	ss >> integerValue;
	return integerValue;
}
void ThreeAddress::createLabels(){
	vector<ThreeAddressInstruction>::iterator it = threeAddressTable.begin();
	int label = 1;
	while( it < threeAddressTable.end() ){
		if ( (*it).getRhs() == "goto" ){
			int line = convertFromStr( (*it).getResult() );
			if ( line >= threeAddressTable.size() ){
				string end("EOF"); 
				(*it).setResult( end );
			} else {
				if((threeAddressTable[line]).getLabel() == 0 ){
					string strLabel = convertToStr( label );
					(threeAddressTable[line]).setLabel( label );
					(*it).setResult( strLabel );
					mapLabelPos[label] = line;
					label++;
				} else {
					string strLabel = convertToStr( (threeAddressTable[line]).getLabel() );
					(*it).setResult( strLabel );
				}
			}
		}
		it++;
	}
	fillMapBBPos();
}

void ThreeAddress::fillMapBBPos(){
	int it = 0, bb = 1;
	if ( threeAddressTable[it].getLhs() != "if" ){
		mapBBlockPos[bb] = it;
		bb++;
		it++;
	}
	while ( it < threeAddressTable.size() ){
		if ( ( (threeAddressTable[it]).getLabel() != 0 ) || ( (threeAddressTable[it]).getLhs() == "if" ) ) { 
			mapBBlockPos[bb] = it;
			bb++;
			if(( (threeAddressTable[it]).getLhs() == "if" ) && ( (threeAddressTable[it+1]).getLhs() != "if" ) ){
				mapBBlockPos[bb] = it + 1;
				bb++;
			}
		}
		it++;
	}
	mapBBlockPos[bb] = it;
	basicBlocks = bb;
}
